#!/usr/bin/env perl

# Copyright (c) 2011, Ryan M. Lefever
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# DESCRIPTION: This program replaces text in one or more files.

# enable perl's safety info
use strict;
use warnings;
use File::Basename;
use SiltCommon;

# Global variables
my @inputFiles;
my @fromStrs;
my @toStrs;

main();
exit(0);

# Parse command line arguments.
sub parsecla{
  my $i;
  for($i = 0; $i <= $#ARGV && $ARGV[$i] ne "--" && ($ARGV[$i] =~ /^-/); ++$i){
    if($ARGV[$i] eq "--help" || $ARGV[$i] eq "-h"){ usage(); exit(0); }
    elsif($ARGV[$i] eq "--verbose") { SiltCommon::setverbose(1); }
    elsif($ARGV[$i] eq "--norun") { SiltCommon::setnorun(1); }
  }

  my $foundFiles = 0;
  my $getToStr = 0;
  for(; $i <= $#ARGV; ++$i){
    if(!$foundFiles){
      if($ARGV[$i] eq "--"){ $foundFiles = 1; }
      else{
	if($getToStr){ push @toStrs, $ARGV[$i]; $getToStr = 0; }
	else{ push @fromStrs, $ARGV[$i]; $getToStr = 1; }
      }
    }
    else{ push @inputFiles, $ARGV[$i]; }
  }

  if($#inputFiles < 0){ print "No input files listed.\n"; usage(); exit(1); }
  if($#fromStrs < 0){ print "No replacements specified.\n"; usage(); exit(1); }
  if($#fromStrs != $#toStrs){
    print "The number of from-strings does not match the number of " .
      "to-strings.\n";
    usage();
    exit(1);
  }
}

sub main{
  parsecla();

  my $i;
  for($i=0; $i <= $#inputFiles; ++$i){
    my ($base, $dir, $suffix) = fileparse($inputFiles[$i]);
    my $tmpFile = $dir . "." . $base . $suffix . "_" . time() . "_" . $$;
    while(-e $tmpFile){ $tmpFile = $tmpFile . int(rand(10)); }
    # In order to get the same access control, copy the original file to
    # the tmp file first
    SiltCommon::runcmd("cp -a " . $inputFiles[$i] . " " . $tmpFile);
    if(SiltCommon::norun() || open(RFH, $inputFiles[$i])){
      if(!SiltCommon::norun() && SiltCommon::verbose()){
	print "Opened " . $inputFiles[$i] . " for reading\n";
      }
      if(SiltCommon::norun() || open(WFH, "> " . $tmpFile)){
	if(!SiltCommon::norun() && SiltCommon::verbose()){
	  print "Opened " . $tmpFile . " for writing\n";
	}

	# Peform replacement in tmp file
	my $line;
	my $fileChanged = 0;
	if(!SiltCommon::norun()){
	  while($line = <RFH>){
	    my $j;
	    for($j=0; $j <= $#fromStrs; ++$j){
	      $_ = $line;
	      if(s/$fromStrs[$j]/$toStrs[$j]/g){ $fileChanged = 1; }
	      $line = $_;
	    }
	    print WFH $line;
	  }
	}
	if(!SiltCommon::norun()){
	  close WFH;
	  close RFH;
	}
	if($fileChanged){ print "Changed " . $inputFiles[$i] . "\n"; }

	# Remove original file
	SiltCommon::runcmd("rm -f " . $inputFiles[$i]);

	# Mv tmp file to original
	SiltCommon::runcmd("mv -f " . $tmpFile . " " . $inputFiles[$i]);
      }
      else{
	close RFH;
	print "Unable to open temporary file " . $tmpFile . "\n";
      }
    }
    else{ print "Unable to open " . $inputFiles[$i] . "\n"; }
  }
}

# Prints usage information
sub usage{
  print "USAGE: replace [OPTIONS] <from str> <to str> [<from str> <to str>] ...
               -- <file> [<file>] ...
DESCRIPTION: Replace from strings with to strings in the specified files.
             This program is part of the SiLT project:
             http://code.google.com/p/silt
OPTIONS:
  --help,-h  print this message
  --norun    do not run commands, most useful with --verbose
  --verbose  enable verbose output
";
}
